﻿using System;
using System.Collections.Generic;
using System.Text;

namespace sign_recognition
{
    public class OutputLayer : Layer
    {
        /// <summary>
        /// Choice parameter >0 The choice parameter  must be greater than zero. It is usually chosen close to zero for a good performance. This parameter ensures that when a point is enclosed in more than one hyperbox, the smallest one is selected.
        /// </summary>
        public Double Alpha;

        /// <summary>
        /// Learning rate parameter [0, 1]
        /// 
        /// When Beta = 1, we are in fast learning.
        /// 
        /// Fast-commit siow-recode option: For efficient coding of noisy input sets, 
        /// it is useful to set Beta = 1 when J is an uncommitted node, and then to take 
        /// Beta < 1 after the category is committed. Then w(j new) = I the first time 
        /// category J becomes active. 
        /// /// </summary>
        public Double Beta;

        /// <summary>
        /// Vigilance parameter [0, 1]
        /// The vigilance parameter has considerable influence 
        /// on the system: higher vigilance produces highly detailed 
        /// memories (many, fine-grained categories), while lower vigilance 
        /// results in more general memories (fewer, more-general categories)
        /// </summary>
        public Double Rho;



        public Double[] I;//input vector 
        public int weightVectorsSize;
        //public Category [] category;
        public List<Category> categories;
        public Double[] y;//y is the output vector of ART
       // public int categoriesAmount;//The number of potential categories N(j = i . . . . . N) is arbitrary

        List<Category> TArray;

        public void updateWeightsOfWinningCategory(Double[] I,String output)
        {
            this.getWinningCategory(output).modifyWeightVector(I, Beta, output);
        }

        public OutputLayer(InputLayer f1, Double alpha, Double rho, Double beta)
        {
            /*this.Alpha = 0.3;//The choice parameter alpha must be greater than zero. It is usually chosen close to zero for a good performance
            this.Rho = 1;
            this.Beta = 0.5;*/

            this.Alpha = alpha;//The choice parameter alpha must be greater than zero. It is usually chosen close to zero for a good performance
            this.Rho = rho;
            this.Beta = beta;

            TArray = new List<Category>();

            this.I = f1.I;//input vector 
            this.weightVectorsSize = f1.M;//The weight vector's size is equal to the dimension M of layer F1.
           // category = new Category[categoriesAmount];//each neuron of layer F2 represents a category formed by the network,
            categories = new List<Category>();

            categories.Add(new Category(this.weightVectorsSize, 0));
    
        }
        public OutputLayer(InputLayer f1, Double alpha, Double rho, Double beta, List<Category> categories)
        {
            /*this.Alpha = 0.3;//The choice parameter alpha must be greater than zero. It is usually chosen close to zero for a good performance
            this.Rho = 1;
            this.Beta = 0.5;*/

            this.Alpha = alpha;//The choice parameter alpha must be greater than zero. It is usually chosen close to zero for a good performance
            this.Rho = rho;
            this.Beta = beta;

            TArray = new List<Category>();

            this.I = f1.I;//input vector 
            this.weightVectorsSize = f1.M;//The weight vector's size is equal to the dimension M of layer F1.
            // category = new Category[categoriesAmount];//each neuron of layer F2 represents a category formed by the network,
            this.categories = new List<Category>();
            this.categories = categories;
           // categories.Add(new Category(this.weightVectorsSize, 0));

        }
        public void receiveInput(InputLayer f1) {

          /*  TArray = new List<Category>();
            //categoriesAmount = 3;
            this.I = f1.I;*/
         
         

       
        }
        /// <summary>
        /// The category choice is indexed by J, where 1": = max{T/:] = 1 . . . N}. (5) If more than one T) is maximal, the category j with the smallest index is chosen. In particular, nodes become committed in order j = 1, 2, 3,... 
        /// 
        /// </summary>
        public void evaluateEachNeuron(){

            TArray.Clear();
            for (int i = 0; i < categories.Count; i++)
            {
                this.categories[i].choiceFunction(I,this.Alpha);
                TArray.Add(this.categories[i]);
                
            }





        }
        /// <summary>
        /// Once a neuron is selected (the winning neuron), 
        /// a vigilance criteron is evaluated. 
        /// The vigilance criterion causes the network to choose another 
        /// box (neuron) if the one chosen is already too large. 
        /// Thus the vigilance parameter Rho 
        /// controls the maximum size of hyperboxes 
        /// If the criterion is respected, the Fuzzy ART learns the input vector, 
        /// else it selects the next neuron with the highest choice 
        /// function and reevaluates the vigilance criterion 
        /// 
        /// </summary>
        public Category getWinningCategory(String expectedOutput)
        {
           // =0;
            Double baseRho = this.Rho;
            TArray.Sort();//If more than one T is maximal, the category j with the smallest index is chosen. In particular, nodes become committed in order j = 1, 2, 3, . . . .  

            Category winningCategory=null;
            for (int i = 0; i < TArray.Count; i++)//The search process continues until the chosen J satisfies criterion
            {
                winningCategory = TArray[i];//A new index J is chosen
                //if (winningCategory.evaluateVigilanceCriterion(this.I, this.Rho))
                if (winningCategory.evaluateVigilanceCriterion(this.I, baseRho))
                {


                    if (winningCategory.output != expectedOutput && !winningCategory.output.Equals("-1"))//If the criterion is not respected, the vigilance of ARTa is increased just enough to select another winning neuron
                    //if (winningCategory.output != expectedOutput || winningCategory.output != -1)//If the criterion is not respected, the vigilance of ARTa is increased just enough to select another winning neuron
                    {
                        baseRho = baseRho - 0.01;
                        winningCategory.T = -1;//reset to - 1 for the duration of the input presentation to prevent its persistent selection during search
                    }
                    else
                    {


                        //Resonance occurs
                        /*If the criterion is respected, the Fuzzy ART learns
                        the input vector, else it selects the next neuron with the highest choice function
                        and reevaluates the vigilance criterion. These two steps are repeated until the
                        vigilance criterion is respected. At that moment, it is said that the network is in
                        resonance.*/
                        Console.WriteLine("Categoría válida existe :) " + winningCategory.output);
                        winningCategory.T = 1;
                        /*All components of the activity pattern on F2, which is also the output
                        vector of the Fuzzy ART, are set to 0 except for the index of the winning neuron
                        which is set to 1.*/
                        for (int j = 1; j < TArray.Count; j++)
                        {//The first one was already selected as the winner
                            ((Category)TArray[j]).T = 0.0;
                        }
                        return winningCategory;
                    }
                }

                else
                {//Mismatch reset
                    //Console.WriteLine("No cumple criterio ");
                    winningCategory.T = -1;//Then the value of the choice function Tj is reset to - 1 for the duration of the input presentation to prevent its persistent selection during search.
        
                }


            }
            //terminó y no encontró, crear una nueva
            Console.WriteLine("Nueva categoría creada ");
            winningCategory = new Category(this.weightVectorsSize, this.categories.Count);
            this.categories.Add(winningCategory);
            return winningCategory;
        }



        public Category clasify()
        {

            TArray.Sort();//If more than one T is maximal, the category j with the smallest index is chosen. In particular, nodes become committed in order j = 1, 2, 3, . . . .  
            foreach(Category i in TArray){
                Console.WriteLine("["+i+"]");
                
            }
            Console.WriteLine("-");
            Category winningCategory =TArray[0];
            return winningCategory;
        }
        public void printWeightsToText(){
            //File fileWeights = new File("Weights.txt");
            File fileCategories = new File("Categories.txt");
            for(int i=0; i<this.categories.Count; i++){
              //  fileWeights.writeWeights(this.categories[i].w);
                fileCategories.writeCategory(this.categories[i]);
            }
         //   fileWeights.close();
            fileCategories.close();
        }
        public void saveWeights(String fileNaname) {
            File fileWeights = new File(fileNaname);
            for (int i = 0; i < this.categories.Count; i++)
            {

                fileWeights.writeWeights(((Category)(this.categories[i])).w, ((Category)(this.categories[i])).output);
            }
          
            fileWeights.close();
        }

        public override string ToString()
        {
            String content = "";
            for (int i = 0; i < this.categories.Count; i++)
            {
                content += this.categories[i].ToString() + "\n";
            }
            return content;
        }
        public void printTArray() {
            for (int i = 0; i < this.TArray.Count; i++ )
            {
                Console.WriteLine("{"+this.TArray[i]+"}");
            }
        }
    }
}
